﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MarsRover
{
    public class Rover : IRover
    {
        private delegate Heading RotationDelegate(Rotation rotation);
        private Dictionary<Heading, RotationDelegate> RotationMap = new Dictionary<Heading, RotationDelegate>();
        private delegate Position MovementDelegate(int x, int y);
        private Dictionary<Heading, MovementDelegate> MovementMap = new Dictionary<Heading, MovementDelegate>();

        private Rover()
        {
        }

        public Rover(int x, int y, Heading heading)
        {
            if (x < 0 || y < 0)
            {
                throw new ArgumentOutOfRangeException("X and Y of position must be greater than zero");
            }

            Position = new MarsRover.Position(x, y);
            Heading = heading;

            MovementMap.Add(MarsRover.Heading.N, (cx, cy) => { return new Position(cx, cy + 1); });
            MovementMap.Add(MarsRover.Heading.E, (cx, cy) => { return new Position(cx + 1, cy); });
            MovementMap.Add(MarsRover.Heading.S, (cx, cy) => { return new Position(cx, cy - 1); });
            MovementMap.Add(MarsRover.Heading.W, (cx, cy) => { return new Position(cx - 1, cy); });

            RotationMap.Add(MarsRover.Heading.N, (r) => 
            {
                return r == Rotation.Left ? MarsRover.Heading.W : MarsRover.Heading.E;
            });

            RotationMap.Add(MarsRover.Heading.E, (r) =>
            {
                return r == Rotation.Left ? MarsRover.Heading.N : MarsRover.Heading.S;
            });

            RotationMap.Add(MarsRover.Heading.S, (r) =>
            {
                return r == Rotation.Left ? MarsRover.Heading.E : MarsRover.Heading.W;
            });

            RotationMap.Add(MarsRover.Heading.W, (r) =>
            {
                return r == Rotation.Left ? MarsRover.Heading.S : MarsRover.Heading.N;
            });
        }

        public Position Position
        {
            get;
            private set;
        }

        public Position GetNextPosition()
        {
            return MovementMap[Heading](Position.X, Position.Y);
        }

        public Heading Heading
        {
            get;
            private set;
        }

        public void RotateLeft()
        {
            Heading = RotationMap[Heading](Rotation.Left);
        }

        public void RotateRight()
        {
            Heading = RotationMap[Heading](Rotation.Right);
        }

        public void Move()
        {
            Position = MovementMap[Heading](Position.X, Position.Y);
        }
    }

    public struct Position
    {
        public Position(int x, int y)
        {
            X = x;
            Y = y;
        }

        public int X;
        public int Y;
    }

    public enum Rotation
    {
        Left,
        Right
    }

    public enum Heading
    {
        N,
        W,
        S,
        E
    }
}
